\chapter{Многопоточное программирование в QNX}
\section{Теоретические сведения}
\subsection{Работа с потоками}

Для создания потока в QNX используется функция pthread\_create() со 
следующей сигнатурой:
\begin{lstlisting}
#include <pthread.h>
int pthread_create( pthread_t* thread,
                    const pthread_attr_t* attr,
                    void* (*start_routine)(void* ),
                    void* arg );
\end{lstlisting}

\argh
\begin{itemize}
\item\textbf{thread} -- NULL, либо указатель на объект pthread\_t, 
где функция может сохранить ID нового потока. 
\item\textbf{attr} -- указатель на элемент стуктуры pthread\_attr\_t, в которой 
описываются атрибуты нового нового потока. Вместо того, чтобы работать с 
элементами данной структуры напрямую, используйте pthread\_attr\_init()
и pthread\_attr\_set\_*. Если attr -- NULL, то используются атрибуты
по умолчанию.
\begin{scriptsize}
Примечание: если вы измените атрибуты в attr после создания потока,
на существующий поток это не повлияет.
\end{scriptsize}
\item\textbf{start\_routine} -- процедура, с которой начинается поток, у которой
arg -- единственный аргумент. Когда start\_routine() заканчивает выполнение,
происходит неявный вызов phtread\_exit(), в котором используется значение,
возвращаемое start\_routine().
\item\textbf{arg} -- аргумент для start\_routine().
\end{itemize}
\textbf{Возвращает:}
\begin{itemize}
\item\textbf{EAGAIN} -- недостаточно ресурсов для создания потока
\item\textbf{EFAULT} -- невозможно получить доступ к памяти или start\_routine.
\item\textbf{EINVAL} -- неверные атрибуты attr.
\item\textbf{EOK} -- удачно.
\end{itemize}

После создания потока он тут же запускается на выполнение. При этом
выполняется код в start\_routine().

Ниже приводится пример программы, в которой запускается один поток:
\lstinputlisting{code/lab3/threads.c}

Функция sleep() освобождает процессор от выполнения данного потока на
заданное количество секунд.

Установка атрибута DETACHED позволяет вызывающему потоку (в данном
случае, функции main) выполняться после вызова phtread\_create при
использовании функции phthread\_join. Возможна была бы ситуация, 
когда после ее вызова вызывающий поток оказался бы 
заблокирован до помента вызова pthread\_exit вызываемым.
\begin{lstlisting}
   pthread_attr_setdetachstate(
      &attr, PTHREAD_CREATE_DETACHED );
\end{lstlisting}

\subsection{Работа с мьютексами}
Мьютексы -- это примитивы синхронизации между потоками. Мьютексы могут 
находиться в одном из двух состояний -- отмеченном или неотмеченном 
(открыт и закрыт соответственно). Когда какой-либо поток, принадлежащий 
любому процессу, становится владельцем объекта mutex (при помощи lock), 
последний переводится в неотмеченное состояние. Если задача 
освобождает мьютекс (unlock), его состояние становится отмеченным.

Задача мьютекса -- защита объекта от доступа к нему других потоков, отличных 
от того, который завладел мьютексом. В каждый конкретный момент только 
один поток может владеть объектом, защищённым мьютексом. Если другому 
потоку будет нужен доступ к переменной, защищённой мьютексом, то этот 
поток засыпает до тех пор, пока мьютекс не будет освобождён.

В QNX для работы с мьютексами используется функции pthread\_mutex\_*.
Они рассмотрены ниже.

\begin{lstlisting}
#include <pthread.h>
int pthread_mutex_lock( pthread_mutex_t* mutex );
\end{lstlisting}

\argh
\begin{\args}
\farg{mutex} -- указатель на объект phread\_mutex\_t, обладание которым
пытается получить поток.
\end{\args}

В противоположность данной функции работает функция phtread\_mutex\_unlock().
\begin{lstlisting}
#include <pthread.h>

int pthread_mutex_unlock( pthread_mutex_t* mutex );
\end{lstlisting}

\argh
\begin{\args}
\farg{mutex} -- указатель на pthread\_mutex\_t, который вы пытаетесь 
разблокировать.
\end{\args}

\lstinputlisting{code/lab3/mutex.c}

Для инициализации мутекса с атрибутами по умолчанию использован макрос
\begin{lstlisting}
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
\end{lstlisting}

\subsection{Работа с семафорами}
Семафоры -- это примитивы синхронизации, позволяющие войти в данную 
критическую секцию (CS) не более, чем $n$ потокам. Принцип работы основан
на инкрементировании счетчика семафора с помощью функции типа post,
и декрементировании -- с помощью функции типа wait. Как только 
счетчик достигает нуля, все потоки, которые пытаются его декрементировать
(с помощью wait), блокируются, пока некий поток, вызвавший ранее wait, 
не вызовет post.

В QNX для работы с семафорами используются функции sem\_*.
\begin{lstlisting}
#include <semaphore.h>
int sem_init( sem_t * sem, 
              int pshared, 
              unsigned value );
\end{lstlisting}
\argh
\begin{\args}
\farg{sem} -- указатель на sem\_t, в котором хранится объект-семафор.
\farg{pshared} -- 0 или 1 -- указывает, можно ли данный семафор использовать
в общей памяти между различными процессами (не потоками).
\farg{value} -- начальное значение семафора -- сколько потоков могут
одновременно получить к нему доступ.
\end{\args}

\begin{lstlisting}
#include <semaphore.h>
int sem_wait( sem_t * sem );
\end{lstlisting}
\argh
\begin{\args}
\farg{sem} -- указатель на семафор, который вы предполагаете <<ожидать>>.
\end{\args}
Данная функция уменьшает счетчик семафора на 1. Если счетчик равен 0,
то поток, вызывающий данную функцию, блокируется.

\begin{lstlisting}
#include <semaphore.h>
int sem_post( sem_t * sem );
\end{lstlisting}
\argh
\begin{\args}
\farg{sem} -- указатель на семафор, который вы предполагаете <<отпустить>>.
\end{\args}

Данная функция увеличивает счетчик семафора на 1. Если счетчик равен
максимально возможному значению дла данного семафора, он не меняется.

Пример программы представлен ниже:
\lstinputlisting{code/lab3/sem.c}

\section{Индивидуальное задание}
В качестве индивидуального задания предлагается написать программы с
использованием мьютексов и семафоров в среде QNX Momenitcs IDE, запустить
и отладить их на удаленной машине с QNX (виртуальной). Варианты индивидуального
задания:
\begin{enumerate}
\item Требуется написать две программы:
\begin{itemize}
\itemСоздать два семафора sem1 и sem2, каждый -- для совместного доступа
одного потока. Создать три потока. Обеспечить совместный
доступ глобальных переменных int i1 и int i2 с помощью sem1 и sem2 
соответственно. При этом поток 1 должен инкрементировать i1 вплоть до 10
каждую секунду, поток 2 -- декрементировать i1 и выводить на экран 
содержимое i1 и i2 каждую секунду на протяжениии 10 секунд, а поток 3 -- 
инкрементировать i2 на протяжении 5 секунд. 
\textbf{Ни один из потоков не должен менять
содержимое i1 и i2 одновременно}.
\itemНаписать двухпоточную программу. Один поток читает с клавиатуры 
структуру следующего вида:
\begin{lstlisting}
struct S
{
  unsigned ui;
  float f;
  unsigned char c;
};
\end{lstlisting}
сопровождая каждое поле запросом и сохраняет записанное в глобальную
переменную типа struct S. Другой поток каждую секунду выводит
содержимое данной переменной в файл. Обеспечить целостность данных
глобальной переменной типа struct S при помощи мутекса. Предусмотреть
возможность выхода из программы. Для этого перед вводом полей 
структуры вывести соответствующий запрос.
\end{itemize}

\item Требуется написать две программы:
\begin{itemize}
\itemСоздать два мутекса m1 и m2, каждый -- для совместного доступа
одного потока. Создать три потока. Обеспечить совместный
доступ глобальных переменных int i1 и int i2 с помощью m1 и m2 
соответственно. При этом поток номер 1 должен инкрементировать i1 до 10
каждую секунду, поток 2 -- декрементировать i1 и выводить на экран 
содержимое i1 и i2 каждую секунду на протяжениии 10 секунд, а поток 3 -- 
инкрементировать i2 на протяжении 7 секунд.

\textbf{Ни один из потоков не должен менять
содержимое i1 и i2 одновременно}.
\itemНаписать двухпоточную программу. Один поток читает с клавиатуры 
структуру следующего вида:
\begin{lstlisting}
struct S
{
  int i;
  double d;
  char c;
};
\end{lstlisting}
сопровождая каждое поле запросом и сохраняет записанное в глобальную
переменную типа struct S. Другой поток каждую секунду выводит
содержимое данной переменной в файл. Обеспечить целостность данных
глобальной переменной типа struct S при помощи семафора. Предусмотреть
возможность выхода из программы. Для этого перед вводом полей 
структуры вывести соответствующий запрос.
\end{itemize}
\end{enumerate}

\begin{scriptsize}
Для того, чтобы немедленно выйти из потока в задаче 2 каждого из
вариантов, воспользуйтесь pthread\_exit(0).
\end{scriptsize}

